// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。go:noescape-

package runtime

import (
	"internal/abi"
	"internal/goarch"
	"unsafe"
)

type mOS struct{}

func thr_new(param *thrparam, size int32) int32

func sigaltstack(new, old *stackt)

// go:noescape-
func sigprocmask(how int32, new, old *sigset)

// go:noescape-
func setitimer(mode int32, new, old *itimerval)

// go:noescape-
func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32

func raiseproc(sig uint32)

func thr_self() thread
func thr_kill(tid thread, sig int)

// go:noescape-
func sys_umtx_op(addr *uint32, mode int32, val uint32, uaddr1 uintptr, ut *umtx_time) int32

func osyield()

// go nosplit 
func osyield_no_g() {
	osyield()
}

func kqueue() int32

// go noescape-
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32

func pipe() (r, w int32, errno int32)
func pipe2(flags int32) (r, w int32, errno int32)
func closeonexec(fd int32)
func setNonblock(fd int32)

// 。h> 
const (
	_CTL_HW      = 6
	_HW_PAGESIZE = 7
)

var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}

// 来自FreeBSD的lib/libc/gen/sysctlnametomib的未记录数字。c、 
const (
	_CTL_QUERY     = 0
	_CTL_QUERY_MIB = 3
)

// sysctlnametomib使用动态分配的sysctl name条目填充mib，
// 返回受影响mib插槽的计数，错误时返回0。
func sysctlnametomib(name []byte, mib *[_CTL_MAXNAME]uint32) uint32 {
	oid := [2]uint32{_CTL_QUERY, _CTL_QUERY_MIB}
	miblen := uintptr(_CTL_MAXNAME)
	if sysctl(&oid[0], 2, (*byte)(unsafe.Pointer(mib)), &miblen, (*byte)(unsafe.Pointer(&name[0])), (uintptr)(len(name))) < 0 {
		return 0
	}
	miblen /= unsafe.Sizeof(uint32(0))
	if miblen <= 0 {
		return 0
	}
	return uint32(miblen)
}

const (
	_CPU_CURRENT_PID = -1 // 当前进程ID.
)

// go:noescape 
func cpuset_getaffinity(level int, which int, id int64, size int, mask *byte) int32

// go:systemstack 
func getncpu() int32 {
	// 为CPU掩码使用大缓冲区。我们在系统
	// 堆栈上，所以这很好，此时我们无法为动态大小的缓冲区分配内存。
	const maxCPUs = 64 * 1024
	var mask [maxCPUs / 8]byte
	var mib [_CTL_MAXNAME]uint32

	// 根据FreeBSD的/usr/src/sys/kern/kern_cpuset。c、 
	// cpuset_getaffinity返回ERANGE当提供的缓冲区大小超过内核中的限制时。
	// 正在查询kern。smp。MaxCPU计算最大缓冲区大小。
	// 请参阅https:

	// 变量kern。smp。MAXCPU于2003年12月23日推出，版本123766，
	// 带有动态分配的sysctl条目。
	miblen := sysctlnametomib([]byte("kern.smp.maxcpus"), &mib)
	if miblen == 0 {
		return 1
	}

	// 查询内核。smp。MaxCPU。
	dstsize := uintptr(4)
	maxcpus := uint32(0)
	if sysctl(&mib[0], miblen, (*byte)(unsafe.Pointer(&maxcpus)), &dstsize, nil, 0) != 0 {
		return 1
	}

	maskSize := int(maxcpus+7) / 8
	if maskSize < goarch.PtrSize {
		maskSize = goarch.PtrSize
	}
	if maskSize > len(mask) {
		maskSize = len(mask)
	}

	if cpuset_getaffinity(_CPU_LEVEL_WHICH, _CPU_WHICH_PID, _CPU_CURRENT_PID,
		maskSize, (*byte)(unsafe.Pointer(&mask[0]))) != 0 {
		return 1
	}
	n := int32(0)
	for _, v := range mask[:maskSize] {
		for v != 0 {
			n += int32(v & 1)
			v >>= 1
		}
	}
	if n == 0 {
		return 1
	}
	return n
}

func getPageSize() uintptr {
	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
	out := uint32(0)
	nout := unsafe.Sizeof(out)
	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
	if ret >= 0 {
		return uintptr(out)
	}
	return 0
}

// FreeBSD的umtx_op系统调用实际上与Linux的futex相同，因此代码基本相似。请参阅Linux实现
// 和lock_futex。请发表评论。

// go:nosplit 
func futexsleep(addr *uint32, val uint32, ns int64) {
	systemstack(func() {
		futexsleep1(addr, val, ns)
	})
}

func futexsleep1(addr *uint32, val uint32, ns int64) {
	var utp *umtx_time
	if ns >= 0 {
		var ut umtx_time
		ut._clockid = _CLOCK_MONOTONIC
		ut._timeout.setNsec(ns)
		utp = &ut
	}
	ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, unsafe.Sizeof(*utp), utp)
	if ret >= 0 || ret == -_EINTR || ret == -_ETIMEDOUT {
		return
	}
	print("umtx_wait addr=", addr, " val=", val, " ret=", ret, "\n")
	*(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005
}

// go:nosplit 
func futexwakeup(addr *uint32, cnt uint32) {
	ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, 0, nil)
	if ret >= 0 {
		return
	}

	systemstack(func() {
		print("umtx_wake_addr=", addr, " ret=", ret, "\n")
	})
}

func thr_start()

// 可以在m.p==nil的情况下运行，因此不允许使用写屏障。
// go:nowritebarrier 
func newosproc(mp *m) {
	stk := unsafe.Pointer(mp.g0.stack.hi)
	if false {
		print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " thr_start=", abi.FuncPCABI0(thr_start), " id=", mp.id, " ostk=", &mp, "\n")
	}

	param := thrparam{
		start_func: abi.FuncPCABI0(thr_start),
		arg:        unsafe.Pointer(mp),
		stack_base: mp.g0.stack.lo,
		stack_size: uintptr(stk) - mp.g0.stack.lo,
		child_tid:  nil, // minit将记录tid 
		parent_tid: nil,
		tls_base:   unsafe.Pointer(&mp.tls[0]),
		tls_size:   unsafe.Sizeof(mp.tls),
	}

	var oset sigset
	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
	ret := thr_new(&param, int32(unsafe.Sizeof(param)))
	sigprocmask(_SIG_SETMASK, &oset, nil)
	if ret < 0 {
		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n")
		throw("newosproc")
	}
}

// newosproc版本，该版本不需要有效的G.
// go:nosplit 
func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
	stack := sysAlloc(stacksize, &memstats.stacks_sys)
	if stack == nil {
		write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
		exit(1)
	}
	// 此代码“知道”它在库
	// 初始化代码中被调用一次，因此，它对
	// tls和procid（线程）指针使用静态m0。thr_new（）需要tls 
	// 指针，但tid指针可以为零。
	// 但是，由于使用c-shared/c-archive强制外部链接来构建
	// ，因此目前无法访问newosproc0。
	param := thrparam{
		start_func: uintptr(fn),
		arg:        nil,
		stack_base: uintptr(stack), // /+堆栈大小？
		stack_size: stacksize,
		child_tid:  nil, // minit将记录tid 
		parent_tid: nil,
		tls_base:   unsafe.Pointer(&m0.tls[0]),
		tls_size:   unsafe.Sizeof(m0.tls),
	}

	var oset sigset
	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
	ret := thr_new(&param, int32(unsafe.Sizeof(param)))
	sigprocmask(_SIG_SETMASK, &oset, nil)
	if ret < 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}
}

var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
var failthreadcreate = []byte("runtime: failed to create new OS thread\n")

// 调用以同步初始化使用
// /-buildmode=c-archive或-buildmode=c-shared生成的Go代码。
// Go运行时均未初始化。
// go:nosplit 
// go:nowritebarrierrec 
func libpreinit() {
	initsig(true)
}

func osinit() {
	ncpu = getncpu()
	if physPageSize == 0 {
		physPageSize = getPageSize()
	}
}

var urandom_dev = []byte("/dev/urandom\x00")

// go:nosplit 
func getRandomData(r []byte) {
	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
	closefd(fd)
	extendRandom(r, int(n))
}

func goenvs() {
	goenvs_unix()
}

// 调用以初始化新的m（包括引导程序m）。
// 在父线程（引导时为主线程）上调用，可以分配内存。
func mpreinit(mp *m) {
	mp.gsignal = malg(32 * 1024)
	mp.gsignal.m = mp
}

// 调用以初始化新的m（包括引导程序m）。
// 在新线程上被调用，无法分配内存。
func minit() {
	getg().m.procid = uint64(thr_self())

	// 在2017年4月之前的FreeBSD上，有一个错误，比如
	// 从主
	// 线程以外的线程调用execve并没有重置信号堆栈。这会混淆
	// minitSignals，它调用minitSignalStack，它检查
	// 当前是否存在信号堆栈，并在
	// 存在时使用它。为了避免这种混淆，当不在
	// 信号堆栈。当我们确信所有
	// 库中运行时，显式禁用主线程上的
	// FreeBSD用户都在运行一个修补过的内核时，就可以删除这个选项。见第15658期。
	if gp := getg(); !isarchive && !islibrary && gp.m == &m0 && gp == gp.m.g0 {
		st := stackt{ss_flags: _SS_DISABLE}
		sigaltstack(&st, nil)
	}

	minitSignals()
}

// 从dropm调用以撤消minit的效果。
// go:nosplit 
func unminit() {
	unminitSignals()
}

// 从exitm调用，而不是从drop调用，以撤销线程拥有的
// minit、semacreate或其他资源的效果。打完电话后不要带锁。
func mdestroy(mp *m) {
}

func sigtramp()

type sigactiont struct {
	sa_handler uintptr
	sa_flags   int32
	sa_mask    sigset
}

// 参见os_freebsd2。走吧，os_freebsd_amd64。go:nowritebarrierrec 

func setsigstack(i uint32) {
	var sa sigactiont
	sigaction(i, nil, &sa)
	if sa.sa_flags&_SA_ONSTACK != 0 {
		return
	}
	sa.sa_flags |= _SA_ONSTACK
	sigaction(i, &sa, nil)
}

// go:nowritebarrierrec 
// go:nowritebarrierrec 
func getsig(i uint32) uintptr {
	var sa sigactiont
	sigaction(i, nil, &sa)
	return sa.sa_handler
}

func setSignalstackSP(s *stackt, sp uintptr) {
	s.ss_sp = sp
}

// go:nosplit 
// go:nowritebarrierrec 
func sigaddset(mask *sigset, i int) {
	mask.__bits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
}

func sigdelset(mask *sigset, i int) {
	mask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
}

// go:nosplit 
func (c *sigctxt) fixsigcode(sig uint32) {
}

func setProcessCPUProfiler(hz int32) {
	setProcessCPUProfilerTimer(hz)
}

func setThreadCPUProfiler(hz int32) {
	setThreadCPUProfilerHz(hz)
}

// go:nosplit 
func validSIGPROF(mp *m, c *sigctxt) bool {
	return true
}

func sysargs(argc int32, argv **byte) {
	n := argc + 1

	// 跳过argv，envp到达auxv 
	for argv_index(argv, n) != nil {
		n++
	}

	// 跳过空分隔符
	n++

	// 现在argv+n是auxv 
	auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize))
	sysauxv(auxv[:])
}

const (
	_AT_NULL     = 0  // 终止向量
	_AT_PAGESZ   = 6  // 以字节为单位的页面大小
	_AT_TIMEKEEP = 22 // 指向timehands的指针。
	_AT_HWCAP    = 25 // CPU功能标志
	_AT_HWCAP2   = 26 // auxv的CPU功能标志2 
)

func sysauxv(auxv []uintptr) {
	for i := 0; auxv[i] != _AT_NULL; i += 2 {
		tag, val := auxv[i], auxv[i+1]
		switch tag {
		// 。org/issue/15206 
		case _AT_PAGESZ:
			physPageSize = val
		case _AT_TIMEKEEP:
			timekeepSharedPage = (*vdsoTimekeep)(unsafe.Pointer(val))
		}

		archauxv(tag, val)
	}
}

// sysSigaction调用sigaction系统调用。
// go:nosplit 
func sysSigaction(sig uint32, new, old *sigactiont) {
	// 使用系统堆栈避免amd64上的拆分堆栈溢出
	if asmSigaction(uintptr(sig), new, old) != 0 {
		systemstack(func() {
			throw("sigaction failed")
		})
	}
}

// asmSigaction在汇编中实现。
// go:noescape 
func asmSigaction(sig uintptr, new, old *sigactiont) int32

// raise向调用线程发送信号。
// 
// 它必须是nosplit，因为它在
// 之前由信号处理程序使用。它肯定有一个Go堆栈。
// 
// go:nosplit 
func raise(sig uint32) {
	thr_kill(thr_self(), int(sig))
}

func signalM(mp *m, sig int) {
	thr_kill(thread(mp.procid), sig)
}

// sigPerThreadSyscall仅在linux上使用，因此我们分配了一个伪信号
// number。
const sigPerThreadSyscall = 1 << 31

// go:nosplit 
func runPerThreadSyscall() {
	throw("runPerThreadSyscall only valid on linux")
}
